package world.ztomorrow.user.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import world.ztomorrow.common.constant.ErrorCodeEnum;
import world.ztomorrow.common.constant.SystemConstant;
import world.ztomorrow.common.dto.RestResp;
import world.ztomorrow.common.exception.BusinessException;
import world.ztomorrow.common.utils.FormatCheckUtils;
import world.ztomorrow.user.utils.JWTUtils;
import world.ztomorrow.user.dao.mapper.UserInfoMapper;
import world.ztomorrow.user.dao.po.UserInfo;
import world.ztomorrow.user.dto.UserInfoDto;
import world.ztomorrow.user.dto.req.UserLoginReqDto;
import world.ztomorrow.user.dto.req.UserRegisterReqDto;
import world.ztomorrow.user.dto.resp.UserLoginRespDto;
import world.ztomorrow.user.dto.resp.UserRegisterRespDto;
import world.ztomorrow.user.service.UserService;
import world.ztomorrow.user.utils.CacheUtils;
import world.ztomorrow.user.utils.RedisVerifyCode;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Objects;

import static world.ztomorrow.common.constant.ErrorCodeEnum.USER_NAME_EXIST;
import static world.ztomorrow.common.constant.ErrorCodeEnum.USER_VERIFY_CODE_ERROR;
import static world.ztomorrow.user.constant.CacheConstant.*;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final RedisVerifyCode redisVerifyCode;
    private final UserInfoMapper userInfoMapper;
    private final CacheUtils cacheUtils;

    @Override
    public RestResp<UserRegisterRespDto> register(UserRegisterReqDto dto) {
        // 校验输入的注册信息
        checkPhoneAndPass(dto.getUsername(), dto.getPassword());
        ErrorCodeEnum codedCheck = FormatCheckUtils.codeFormat(dto.getVelCode());
        if (codedCheck != null){
            throw new BusinessException(codedCheck);
        }
        // 校验验证码
        if (!redisVerifyCode.checkVerifyCode(dto.getSessionId(), dto.getVelCode())){
            throw new BusinessException(USER_VERIFY_CODE_ERROR);
        }
        // 主动释放已经使用的验证码缓存
        redisVerifyCode.delVerifyCode(dto.getSessionId());
        // 检查用户是否存在
        Long count = new LambdaQueryChainWrapper<>(userInfoMapper)
                .eq(UserInfo::getUsername, dto.getUsername())
                .count();
        if (count > 0){
            throw new BusinessException(USER_NAME_EXIST);
        }
        // 创建用户
        UserInfo userInfo = new UserInfo();
        userInfo.setPassword(
                DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)));
        userInfo.setUsername(dto.getUsername());
        userInfo.setNickName(dto.getUsername());
        userInfo.setCreateTime(LocalDateTime.now());
        userInfo.setUpdateTime(LocalDateTime.now());
        userInfo.setSalt("0");
        userInfoMapper.insert(userInfo);
        // 创建JWT令牌，发送给前端

        return RestResp.ok(new UserRegisterRespDto
                (userInfo.getId(), JWTUtils.createJWT(userInfo.getId(), SystemConstant.NOVEL_FRONT_KEY)));
    }

    @Override
    public RestResp<UserLoginRespDto> login(UserLoginReqDto dto) {
        // 校验输入信息格式
        checkPhoneAndPass(dto.getUsername(), dto.getPassword());
        // 检查用户是否存在
        UserInfo userInfo = new LambdaQueryChainWrapper<>(userInfoMapper)
                .eq(UserInfo::getUsername, dto.getUsername())
                .one();
        if (Objects.isNull(userInfo)) {
            // 用户不存在
            throw new BusinessException(ErrorCodeEnum.USER_ACCOUNT_NOT_EXIST);
        }
        // 检查密码是否正确
        if (!Objects.equals(userInfo.getPassword()
                , DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)))) {
            // 密码错误
            throw new BusinessException(ErrorCodeEnum.USER_PASSWORD_ERROR);
        }

        // 登录成功，生成JWT并返回
        return RestResp.ok(
                new UserLoginRespDto(
                        userInfo.getId(),
                        userInfo.getNickName(),
                        JWTUtils.createJWT(userInfo.getId(), SystemConstant.NOVEL_FRONT_KEY)
                )
        );
    }

    private void checkPhoneAndPass(String username, String password) {
        ErrorCodeEnum phoneCheck = FormatCheckUtils.phoneFormat(username);
        if (phoneCheck != null) {
            throw new BusinessException(phoneCheck);
        }
        ErrorCodeEnum passwordCheck = FormatCheckUtils.passwordFormat(password);
        if (passwordCheck != null) {
            throw new BusinessException(passwordCheck);
        }
    }

    public UserInfoDto getUser(Long userId) {
        UserInfoDto cacheOne = cacheUtils.getCacheOne(
                v -> BeanUtil.copyProperties(v, UserInfoDto.class),
                CAFFEINE_USER_INFO, REDIS_USER_INFO,
                v -> JSONUtil.toBean(v, UserInfoDto.class));
        if (cacheOne != null) {
            return cacheOne;
        }
        UserInfo userInfo = userInfoMapper.selectById(userId);
        if (Objects.isNull(userInfo)) {
            return null;
        }
        cacheUtils.saveCacheOne(
                userInfo, CAFFEINE_USER_INFO,
                REDIS_USER_INFO, REDIS_USER_INFO_TTL,
                v -> JSONUtil.toJsonStr(userInfo));
        return UserInfoDto.builder()
                .id(userInfo.getId())
                .status(userInfo.getStatus()).build();
    }
}
